package jmine.tec.rpc.server.representers;

import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;

import java.io.IOException;
import java.io.InputStream;
import java.text.ParseException;

import jmine.tec.rpc.common.entity.EntityRest;
import jmine.tec.rpc.common.lazy.LazyRepresentation;
import jmine.tec.rpc.common.provider.RestServiceProvider;
import jmine.tec.rpc.common.serialization.MockBean;
import jmine.tec.rpc.common.serialization.MockBeanBuilder;
import jmine.tec.rpc.common.serialization.Serializer;
import jmine.tec.rpc.common.serialization.SerializerBuilder;
import jmine.tec.rpc.server.representation.RepresentationFactory;

import org.jmock.Expectations;
import org.jmock.Mockery;
import org.jmock.integration.junit4.JMock;
import org.jmock.integration.junit4.JUnit4Mockery;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;

/**
 * Base conversion test
 * 
 * @author lundberg
 */
@RunWith(JMock.class)
public class ALazyCapableSerializerShould {

    private final Mockery context = new JUnit4Mockery();

    private RestServiceProvider provider = this.context.mock(RestServiceProvider.class);

    private EntityRest<?> service = this.context.mock(EntityRest.class);

    private RepresentationFactory factory;

    private Serializer serializer;

    private MockBean bean;

    private MockBean bean2;

    private MockBean bean3;

    private MockBean bean4;

    /**
     * Initializes the serializer
     */
    @Before
    public void init() {
        this.factory = LazyRepresentationFactoryBuilder.aLazyRepresentationFactory().with(this.provider).build();
        this.serializer = SerializerBuilder.aLazyCapableSerializer(this.provider).build();
        Long id = 1L;
        this.bean = MockBeanBuilder.aBean().withId(id++).build();
        this.bean2 = MockBeanBuilder.aBean().withId(id++).build();
        this.bean3 = MockBeanBuilder.aBean().withId(id++).build();
        this.bean4 = MockBeanBuilder.aBean().withId(id++).build();
        this.bean.getObjectList().add(this.bean2);
        this.bean.getSet().add(this.bean3);
        this.bean.getMap().put("key", this.bean4);
    }

    /**
     * Verifica se o serializador lida com proxies adequadamente
     * 
     * @throws Exception Exception
     */
    @Test
    public void handleProxiesWithoutResolving() throws Exception {
        this.context.checking(new Expectations() {
            {
                this.atLeast(1).of(ALazyCapableSerializerShould.this.provider).provideByRepresentation(MockRepresentation.class);
                this.will(returnValue(ALazyCapableSerializerShould.this.service));

                this.atLeast(1).of(ALazyCapableSerializerShould.this.service).getRepresentationClass();
                this.will(returnValue(MockRepresentation.class));
            }
        });
        MockRepresentation representation = this.factory.representObject(MockRepresentation.class, this.bean);
        MockRepresentation deserialized = this.serializeAndDeserialize(representation);
        assertThat(deserialized.getObjectList().get(0) instanceof LazyRepresentation, is(true));
    }

    /**
     * Verifica se o serializador lida com proxies adequadamente
     * 
     * @throws Exception Exception
     */
    @Test
    public void deserializedProxyShouldBeEqual() throws Exception {
        this.context.checking(new Expectations() {
            {
                this.atLeast(1).of(ALazyCapableSerializerShould.this.provider).provideByRepresentation(MockRepresentation.class);
                this.will(returnValue(ALazyCapableSerializerShould.this.service));

                this.atLeast(1).of(ALazyCapableSerializerShould.this.service).getRepresentationClass();
                this.will(returnValue(MockRepresentation.class));

                this.atLeast(1).of(ALazyCapableSerializerShould.this.service)
                        .findByIdentification(ALazyCapableSerializerShould.this.bean2.getId());
                this.will(returnValue(ALazyCapableSerializerShould.this.factory.representObject(MockRepresentation.class,
                        ALazyCapableSerializerShould.this.bean2)));

                this.atLeast(1).of(ALazyCapableSerializerShould.this.service)
                        .findByIdentification(ALazyCapableSerializerShould.this.bean4.getId());
                this.will(returnValue(ALazyCapableSerializerShould.this.factory.representObject(MockRepresentation.class,
                        ALazyCapableSerializerShould.this.bean4)));
            }
        });
        MockRepresentation representation = this.factory.representObject(MockRepresentation.class, this.bean);
        MockRepresentation deserialized = this.serializeAndDeserialize(representation);
        assertThat(deserialized, is(equalTo(representation)));
    }

    /**
     * @param original original
     * @return result
     */
    private MockRepresentation serializeAndDeserialize(MockRepresentation original) {
        try {
            InputStream stream = this.serializer.serialize(original);
            return (MockRepresentation) this.serializer.deserialize(stream);
        } catch (IOException e) {
            fail(e.getMessage());
            throw new IllegalStateException();
        } catch (ParseException e) {
            fail(e.getMessage());
            throw new IllegalStateException();
        }
    }
}
